import { expect } from 'chai';
import sinon from 'sinon';
import { uploadToPackageFileStorage } from '../src/package-file-upload';
import axios from 'axios';
import fs from 'fs';
import * as step from '@flow-step/step-toolkit'
import {UploadInputs} from '../src/upload-inputs'

describe('uploadToPackageFileStorage', () => {
    const artifact = {
        authorization: 'test-auth',
        baseUrl: 'https://test-url',
        artifactFullName: 'test-file.txt',
        artifact: 'test-artifact',
        artifactName: 'test-artifact-name',
        artifactVersion: '1.0.0',
        fileMD5: 'test-md5',
        stepIdentifier: 'test-step',
        uploadType: 'test-type',
        repoId: 'test-repo',
        fileId: 'test-file-id',
        bucketName: 'test-bucket',
        fileParentId: 'test-parent-id',
        projectDir: 'test-project-dir'
    } as UploadInputs;

    let axiosStub: sinon.SinonStub;
    let createReadStreamStub: sinon.SinonStub;
    let unlinkSyncStub: sinon.SinonStub;

    beforeEach(() => {
        axiosStub = sinon.stub(axios, 'request');
        createReadStreamStub = sinon.stub(fs, 'createReadStream');
        unlinkSyncStub = sinon.stub(fs, 'unlinkSync');
        sinon.stub(step, 'error');
        sinon.stub(step, 'info');
        sinon.stub(step, 'addOutput');
    });

    afterEach(() => {
        axiosStub.restore();
        createReadStreamStub.restore();
        unlinkSyncStub.restore();
        (step.error as sinon.SinonStub).restore();
        (step.info as sinon.SinonStub).restore();
        (step.addOutput as sinon.SinonStub).restore();
    });

    it('should throw an error if authorization is undefined', async () => {
        const artifactWithoutAuth = { ...artifact, authorization: undefined } as UploadInputs;
        try {
            await uploadToPackageFileStorage(artifactWithoutAuth);
            expect.fail('Expected error to be thrown');
        } catch (error) {
            // @ts-ignore
            expect(error.message).to.equal('PACKAGES_AUTHORIZATION is empty');
        }
    });

    it('should throw an error if baseUrl is undefined', async () => {
        const artifactWithoutBaseUrl = { ...artifact, baseUrl: undefined } as UploadInputs;
        try {
            await uploadToPackageFileStorage(artifactWithoutBaseUrl);
            expect.fail('Expected error to be thrown');
        } catch (error) {
            // @ts-ignore
            expect(error.message).to.equal('PACKAGES_BASE_URL is empty');
        }
    });

    it('should throw an error if MD5 hash does not match', async () => {
        axiosStub.resolves({ data: { object: { fileMd5: 'mismatched-md5', url: 'test-url' } } });
        createReadStreamStub.returns('test-stream');

        try {
            await uploadToPackageFileStorage(artifact);
            expect.fail('Expected error to be thrown');
        } catch (error) {
            // @ts-ignore
            expect(error.message).to.equal('Failed to upload artifact');
        }
    });

    it('should successfully upload artifact and set outputs', async () => {
        axiosStub.resolves({ data: { object: { fileMd5: 'test-md5', url: 'test-url', fileSize: 1234 } } });
        createReadStreamStub.returns('test-stream');

        await uploadToPackageFileStorage(artifact);

          expect(unlinkSyncStub.calledOnceWith(artifact.artifactFullName)).to.be.true;
    });

    it('should throw an error if upload fails', async () => {
        axiosStub.rejects({ message: 'Network Error', response: { data: 'Failed to upload' } });
        createReadStreamStub.returns('test-stream');

        try {
            await uploadToPackageFileStorage(artifact);
            expect.fail('Expected error to be thrown');
        } catch (error) {
            // @ts-ignore
            expect(error.message).to.equal('Failed to upload artifact');
        }
    });
});
